//package com.demo.uid.config;
//
//import com.demo.uid.constant.ConfigConstant;
//import com.demo.uid.service.SnowFlakeService;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.springframework.beans.factory.InitializingBean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.Resource;
//import java.net.InetAddress;
//import java.net.UnknownHostException;
//import java.util.Date;
//import java.util.Timer;
//import java.util.TimerTask;
//import java.util.concurrent.TimeUnit;
//
///**
// * 自动配置机器ID
// */
//@Component
//@Configuration
//@Slf4j
//public class MachineIdConfig implements InitializingBean {
//
//    /**
//     * 机器id
//     */
//    private Integer machineId;
//    /**
//     * 本地ip地址
//     */
//    private String localIp;
//
//    /**
//     * StringRedisTemplate 的默认序列化方式为 StringRedisSerializer
//     */
//    @Resource
//    private StringRedisTemplate stringRedisTemplate;
//
//    @Resource
//    private SnowFlakeService snowFlakeService;
//
//    /**
//     * hash机器IP初始化一个机器ID
//     * @throws Exception
//     */
//    @Override
//    public void afterPropertiesSet() throws Exception {
//        // 192.168.0.233
//        localIp = getIPAddress();
//        // 本地ip地址 转换  1921680233
//        Long ip = Long.parseLong(localIp.replaceAll("\\.", ""));
//        // 机器id 取模 0-31
//        machineId = ip.hashCode() % 32;
//        // 创建一个机器ID
//        createMachineId();
//        log.info("初始化 machine_id :{}", machineId);
//        snowFlakeService.init(machineId);
//    }
//
//    /**
//     * 获取ip地址
//     *
//     * @return
//     * @throws UnknownHostException
//     */
//    private String getIPAddress() throws UnknownHostException {
//        InetAddress address = InetAddress.getLocalHost();
//        return address.getHostAddress();
//    }
//
//    /**
//     * 主方法：首先获取机器 IP 并 % 32 得到 0-31
//     * 使用 机器id + IP 作为 Redis 的 key，机器IP作为 value，存储到Redis中
//     *
//     * @return
//     */
//    private Integer createMachineId() {
//        try {
//            // 向redis注册，并设置超时时间
//            log.info("注册一个机器ID到Redis, 机器id：{}, IP:{}", machineId ,localIp);
//            Boolean flag = registerMachine( machineId, localIp);
//            // 注册成功
//            if (flag) {
//                // 启动一个线程更新超时时间
//                updateExpTimeThread();
//                // 返回机器Id
//                log.info("Redis中端口没有冲突 机器id：{}，IP：{}", machineId, localIp);
//                return machineId;
//            }
//            // 注册失败，可能原因 Hash%32 的结果冲突
//            if (!checkIfCanRegister()) {
//                // 如果 0-31 已经用完，使用 32-64之间随机的ID
//                getRandomMachineId();
//                createMachineId();
//            } else {
//                // 如果存在剩余的ID
//                log.warn("Redis中端口冲突了，使用 0-31 之间未占用的机器Id：{}，IP：{} ", machineId, localIp);
//                createMachineId();
//            }
//        } catch (Exception e) {
//            // 获取 32 - 63 之间的随机Id
//            // 返回机器Id
//            log.error("Redis连接异常,不能正确注册雪花机器号:{}, IP:{} ", machineId , localIp, e);
//            log.warn("使用临时方案，获取 32 - 63 之间的随机数作为机器号，请及时检查Redis连接");
//            getRandomMachineId();
//            return machineId;
//        }
//        return machineId;
//    }
//
//    /**
//     * 1.注册机器
//     * 2.设置超时时间
//     *
//     * @param machineId 取值为0~31
//     * @return
//     */
//    private Boolean registerMachine(Integer machineId, String localIp) {
//        // key 数据中心ID + 机器ID value 机器IP
//        String key = String.valueOf(ConfigConstant.dataCenterId + machineId);
//        Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(key,localIp);
//
//        if (absent) {
//            // 过期时间 1 天
//            stringRedisTemplate.expire(key, 1, TimeUnit.DAYS);
//            return true;
//        } else {
//            // 如果Key存在，判断Value和当前IP是否一致，一致则返回True
//            String value = stringRedisTemplate.opsForValue().get(key);
//            if (StringUtils.equals(localIp, value)) {
//                // IP一致，注册机器ID成功
//                stringRedisTemplate.expire(key, 1, TimeUnit.DAYS);
//                return true;
//            }
//            return false;
//        }
//    }
//
//    /**
//     * 1.更新超時時間
//     * 注意，更新前检查是否存在机器ip占用情况
//     */
//    private void updateExpTimeThread() {
//        // 开启一个线程执行定时任务:
//        // 每23小时更新一次超时时间
//        new Timer(localIp).schedule(new TimerTask() {
//            @Override
//            public void run() {
//                // 检查缓存中的ip与本机ip是否一致, 一致则更新时间，不一致则重新获取一个机器id
//                Boolean b = checkIsLocalIp(machineId);
//                if (b) {
//                    log.info("IP一致，更新超时时间 ip:{},machineId:{}, time:{}", localIp, machineId, new Date());
//                    stringRedisTemplate.expire(String.valueOf(ConfigConstant.dataCenterId + machineId), 1, TimeUnit.DAYS);
//                } else {
//                    // IP冲突
//                    log.info("重新生成机器ID ip:{},machineId:{}, time:{}", localIp, machineId, new Date());
//                    // 重新生成机器ID，并且更改雪花中的机器ID
//                    getRandomMachineId();
//                    // 重新生成并注册机器id
//                    createMachineId();
//                    // 更改雪花中的机器ID
//                    snowFlakeService.updateSnowFlakeMachineId(machineId);
//                    // 结束当前任务
//                    log.info("Timer->thread->name:{}", Thread.currentThread().getName());
//                    this.cancel();
//                }
//            }
//        }, 10 * 1000, 1000 * 60 * 60 * 23);
//    }
//
//    /**
//     * 检查Redis中对应Key的Value是否是本机IP
//     *
//     * @param machineId
//     * @return
//     */
//    private Boolean checkIsLocalIp(Integer machineId) {
//        String ip = stringRedisTemplate.opsForValue().get(String.valueOf(ConfigConstant.dataCenterId + machineId));
//        log.info("checkIsLocalIp->ip:{}", ip);
//        return localIp.equals(ip);
//    }
//
//    /**
//     * 获取32-63随机数
//     */
//    private void getRandomMachineId() {
//        machineId = (int) (Math.random() * 31) + 31;
//    }
//
//    /**
//     * 检查是否被注册满了
//     *
//     * @return
//     */
//    private Boolean checkIfCanRegister() {
//        boolean flag = true;
//        // 判断0~31这个区间段的机器IP是否被占满
//        for (int i = 0; i < 32; i++) {
//            flag = stringRedisTemplate.hasKey(String.valueOf(ConfigConstant.dataCenterId + i));
//            // 如果不存在。设置机器Id为这个不存在的数字
//            if (!flag) {
//                machineId = i;
//                break;
//            }
//        }
//        return !flag;
//    }
//
//    public Integer getMachineId() {
//        return machineId;
//    }
//}
